<?php
    /**
     *  Copyright (c) 2013, dsphinx@plug.gr
     *  All rights reserved.
     *
     *  Redistribution and use in source and binary forms, with or without
     *  modification, are permitted provided that the following conditions are met:
     *   1. Redistributions of source code must retain the above copyright
     *      notice, this list of conditions and the following disclaimer.
     *   2. Redistributions in binary form must reproduce the above copyright
     *      notice, this list of conditions and the following disclaimer in the
     *      documentation and/or other materials provided with the distribution.
     *   3. All advertising materials mentioning features or use of this software
     *      must display the following acknowledgement:
     *      This product includes software developed by the dsphinx.
     *   4. Neither the name of the dsphinx nor the
     *      names of its contributors may be used to endorse or promote products
     *     derived from this software without specific prior written permission.
     *
     *  THIS SOFTWARE IS PROVIDED BY dsphinx ''AS IS'' AND ANY
     *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     *  DISCLAIMED. IN NO EVENT SHALL dsphinx BE LIABLE FOR ANY
     *  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     *
     */

    class Auth extends Sessions
    {

        const        LOCK_AFTER_FAILED_LOGIN = 6;
        const        PASSWORD_MIN_LENGTH = 7;
        static $PASSWORD_MESSAGE = NULL;


        static public function isLoggedIN()
        {

            self::init();
            $ret = FALSE;
            if (empty($_SESSION['Auth']['User_Session_Id']) || empty($_SESSION['Auth']['UserName']))
                return $ret;
            //Single User only
            if ($_SESSION['Auth']['User_Session_Id'] == session_id() &&
                $_SESSION['Auth']['UserName'] != "" &&
                $_SESSION['Auth']['User_Session_Id'] == self::get_info("session_id")
            ) // Only 1 USer
                // Multiuser logging
                //	if ($_SESSION['Auth']['User_Session_Id']==session_id()  && $_SESSION['Auth']['UserName']!="")
                $ret = TRUE;

            return $ret;
        }


        static public function init()
        {

            if (!Cookies::Exists('Auth_Tries') || !isset($_SESSION['Auth_Tries'])) { //  			 **** 	Πρώτη επίσκεψη / κλήση
                $php_key = 1;
                Cookies::Set('Auth_Tries', $php_key, Cookies::Session); //					Α)   Δημιουργία  cookies
                $_SESSION['Auth_Tries'] = $php_key;
            } else
                $_SESSION['Auth_Tries'] = $_SESSION['Auth_Tries'] + 1;

            if (isset($_POST['logout']) || isset($_GET['logout'])) {
                self::logout();

                return;
            }


        }

        static public function logout()
        {
            $db = new MyDB();

            $username = $_SESSION['Auth']['UserName'];
            self::_set_sessions();

            unset($_SESSION['Auth_Tries']);
            unset($_SESSION['Auth']);

            Cookies::Delete('Auth_Tries');


            $db->where('email', $username);
            if (!$db->update("Auth", array('session_id' => '')))
                trigger_error("ERROR  logging off  $username ! " . $db->error());

            return TRUE;
        }

        /**
         * @param null $sessions
         * @param null $username
         * @param null $level
         *
         * Prevent's session fixation
         * Set SEssions Variables
         *
         */
        static public function _set_sessions($sessions = NULL, $username = NULL, $level = NULL)
        {

            if ($sessions) {
                session_regenerate_id(TRUE); // Prevent's session fixation
            } else {
                $TMP = $_SESSION['PATHS'];

                session_regenerate_id(TRUE);
                $_SESSION['PATHS'] = $TMP;
            }

            $_SESSION['Auth'] =
                array(
                    "User_Session_Id" => $sessions,
                    "UserName"        => $username,
                    "Level"           => $level
                );

            return;
        }

        static public function get_level()
        {
            return isset($_SESSION['Auth']) ? intval($_SESSION['Auth']['Level']) : NULL;
        }

        static public function get_info($field = "surname")
        {

            $ret = NULL;
            $db  = new MyDB();

            $db->where('email', self::get_username());
            $results = $db->query(" SELECT * FROM  `Auth`");

            if ($results)
                return $results[0][$field];
        }


        /**
         * @param        $id
         * @param string $field
         *
         *  για αθτη genik;a
         *
         * @return mixed
         *
         */
        static public function get_info_guest($id, $field = "surname")
        {

            $ret = NULL;
            $db  = new MyDB();

            $db->where('id', $id);
            $results = $db->query(" SELECT * FROM  `Auth`");

            if ($results)
                return $results[0][$field];
        }

        static public function get_username()
        {
            return isset($_SESSION['Auth']) ? $_SESSION['Auth']['UserName'] : NULL;
        }


        /**
         * @param        $password
         * @param null   $salt
         * @param string $algorythm
         *
         *
         *  Return SHA1 salted Password
         *
         *          SHA1 Salted Password
         *
         * @return string
         */

        static public function salt_password($password, $salt = NULL, $algorythm = "sha256")
        {

            $salted_password = hash($algorythm, $password);
          //  $salted_password =  $password;

            return $salted_password;
        }


        static public function Logging($section, $message, $table = "Logging", $geolocation = NULL)
        {

            $db   = new MyDB();
            $_ret = TRUE;

            $record                = array();
            $record['section']     = $section;
            $record['ip']          = getenv("REMOTE_ADDR");
            $record['browser']     = getenv("HTTP_USER_AGENT");
            $record['message']     = $message;
            $record['coordinates'] = $geolocation;

            if (!$db->insert($table, $record))
                $_ret = FALSE;

            return $_ret;
        }


        /**
         *   Κατώφλι
         *
         */
        static public function validation_tried()
        {

            $_ret = TRUE;

            self::init();

            if (isset($_SESSION['Auth_Tries']))
                if ($_SESSION['Auth_Tries'] + 0 == self::LOCK_AFTER_FAILED_LOGIN) {

                    self::Logging('Authentication', 'Brute Force Attack, Locked Sessions too many tries');
                    $_SESSION['Auth_Tries'] = $_SESSION['Auth_Tries'] + 1;

                    // TODO
                    //           mail username for attacked account
                    //
                    $_ret = FALSE;
                } elseif ($_SESSION['Auth_Tries'] + 0 > self::LOCK_AFTER_FAILED_LOGIN) {
                    // TODO
                    //			 do nothing ?
                    //
                    $_ret = FALSE;
                }

            return $_ret;

        }


        /**
         * @param        $password
         * @param string $password_verification
         * @param null   $userid
         *
         *  Change user password
         *
         *
         * @return bool
         */
        public static function changepassword($password, $password_verification = "", $userid = NULL)
        {

            $ret = FALSE;

            if (empty($password_verification))
                return $ret;
            else
                if (strcmp($password_verification, $password) != 0)
                    return $ret;

            $crypted_passwd = self::salt_password($password);

            $db            = new MyDB();
            $rec           = array();
            $rec['passwd'] = $crypted_passwd;

            if ($userid)
                $db->where('id', $userid);
            else
                $db->where('email', self::get_username());


            if (!$db->update("Auth", $rec))
                $ret = FALSE;
            else
                $ret = TRUE;

            $db->close();

            return $ret;
        }


        static public function validation($loginname, $pass, $geolocation = NULL)
        {

            $db   = new MyDB();
            $_ret = TRUE;

            if (!self::validation_tried()) {
                return self::validation_tried();
            }


            $pass_enc = self::salt_password($pass);
            $db->where('email', $loginname);
            $results = $db->query(" SELECT passwd,locked,ipallow,level,counter,id FROM  `Auth`");

             trigger_error(__FILE__ ."  ".$loginname. "  - ". $results[0]['passwd'] . '  == ' . $pass_enc);

            if (strcmp($results[0]['passwd'], $pass_enc) === 0) { //   Έγκυρη Πιστοποίηση  &&	  Επιπλέον ρυθμίσεις ασφαλείας

                if ($results[0]['locked'] == 1) { // Ενεργός ?
                    self::Logging('Authentication', " User " . $loginname . " has been LOCKED by administrator ! trying to connect ");
                    $_ret = FALSE;
                }

                if ($results[0]['ipallow'] != "") //  Μη έκγυρη  IP
                    if ($results[0]['ipallow'] != getenv("REMOTE_ADDR")) {
                        self::Logging('Authentication', " User " . $loginname . " has logged in from disallowed IP address ! rejected ");

                        $_ret = FALSE;
                    }

                //		self::set_sessions(session_id(),$user_name1,$results[3]);
                // AKAMAI
                //self::_set_sessions(session_id($_COOKIE['PHPSESSID']), $loginname, $results[0]['level']);
                self::_set_sessions(TRUE, $loginname, $results[0]['level']);
                $cx = $results[0]['counter'] + 1;

                $rec               = array();
                $rec['session_id'] = session_id();
                $rec['counter']    = $cx;

                $db->where('email', $loginname);
                if (!$db->update("Auth", $rec))
                    echo "ERROR Updating  User!! " . __FILE__;

                $record                = array();
                $record['Auth_id']     = $results[0]['id'];
                $record['IP']          = getenv("REMOTE_ADDR");
                $record['User_Agent']  = getenv("HTTP_USER_AGENT");
                $record['Geolocation'] = $geolocation;

                if (!$db->insert("Auth_Logging", $record))
                    trigger_error(" Auth Logging error " . __FILE__);


                $db->close();

                $_SESSION['Auth_Tries'] = 1;

            } else { // Αποτυχία Πιστοποίησης
                unset($_SESSION['Auth']);
                if (isset($_SESSION['Auth_Tries']))
                    $_SESSION['Auth_Tries'] = $_SESSION['Auth_Tries'] + 1;
                else
                    $_SESSION['Auth_Tries'] = 0;

                self::Logging('Authentication', "FAILED, Tried [" . $_SESSION['Auth_Tries'] . "]  login [" . $loginname . "] passwd [" . $pass . "]");

                $_ret = FALSE;
            }

            return $_ret;
        }

        /**
         * @param null $pass
         *
         *  Password unit test
         *
         * @return bool
         */
        static function password_rules($pass = NULL)
        {

            $_ret = TRUE;

            $length_pass          = (strlen($pass) > self::PASSWORD_MIN_LENGTH) ? TRUE : FALSE;
            $all_str_pass         = ctype_alpha($pass);
            $all_num_pass         = ctype_alnum($pass);
            $all_ctrl_pass        = ctype_cntrl($pass);
            $all_dgts_pass        = ctype_digit($pass);
            $all_punt_pass        = ctype_punct($pass);
            $all_whitespaces_pass = ctype_space($pass);

            if ($all_str_pass || $all_ctrl_pass || $all_dgts_pass || $all_num_pass || $all_punt_pass || $all_whitespaces_pass) {
                $_ret                   = FALSE;
                self::$PASSWORD_MESSAGE = " All Characters are the same category !, please retry ..";
            }

            if (!$length_pass) {
                $_ret                   = FALSE;
                self::$PASSWORD_MESSAGE = " Password must be greater than " . self::PASSWORD_MIN_LENGTH;
            }

            return $_ret;
        }


        static function password_rules_text()
        {
            return self::$PASSWORD_MESSAGE;
        }

    }